grammar CACT;

@header {
	#include "HExpression.h"
	#include "HFunctionTable.h"
}

/* paser */

comp_unit
	locals [
		HSymbolTable* scope,
	]
	: (decl|func_def)* EOF
	;

decl
	: const_decl
	| var_decl
	;

const_decl
	: CONST b_type const_def (Comma const_def)* Semicolon
	;

b_type
	locals [
		/* To pass base type to const/var_def in const/var_decl */
		std::vector<Btype *> pass_to,
	]
	: INT
	| BOOL
	| FLOAT
	| DOUBLE
	;

const_def
	locals [
		cact_type_t ctype,
	]
	: ID (LSqr Int_const RSqr)* AssignOp const_init_val
	;

const_init_val
	locals [
		cact_type_t ctype,
	]
	: const_expr
	| LCur (const_init_val (Comma const_init_val)*)? RCur
	;

var_decl
	: b_type var_def (Comma var_def)* Semicolon
	;

var_def
	locals [
		cact_type_t vtype,
	]
	: ID (LSqr Int_const RSqr)* (AssignOp const_init_val)?
	;

func_def
	locals [
		size_t hfteidx,
	]
	: func_type ID LPar (func_params)? RPar block
	;

func_type
	: VOID
	| INT
	| FLOAT
	| DOUBLE
	| BOOL
	;

func_params
	: func_param (Comma func_param)*
	;

func_param
	locals [
		cact_type_t ptype,
	]
	: b_type ID (LSqr Int_const? RSqr (LSqr Int_const RSqr)*)?
	;

block
	locals [
		HSymbolTable* scope,
	]
	: LCur (block_item)* RCur
	;

block_item
	: decl
	| stmt
	;

stmt
	: lval AssignOp expr Semicolon
	| expr? Semicolon
	| block
	| RETURN expr? Semicolon
	| IF LPar expr RPar stmt (ELSE stmt)?
	| WHILE LPar expr RPar stmt
	| BREAK Semicolon
	| CONTINUE Semicolon
	;

lval
	locals [
		HExpression *hexpr,
	]
	: ID (LSqr expr RSqr)*
	;

expr
	locals [
		HExpression *hexpr,
	]
	: lg_or_expr
	;

lg_or_expr
	locals [
		HExpression *hexpr,
	]
	: lg_and_expr
	| lg_or_expr LgOrOp lg_and_expr
	;

lg_and_expr
	locals [
		HExpression *hexpr,
	]
	: eq_expr
	| lg_and_expr LgAndOp eq_expr
	;

eq_expr
	locals [
		HExpression *hexpr,
	]
	: rel_expr
	| eq_expr EqOp rel_expr
	;

/* Assume that "1<2<3" is illegal */
rel_expr
	locals [
		HExpression *hexpr,
	]
	: add_expr
	| add_expr RelOp add_expr
	;

add_expr
	locals [
		HExpression *hexpr,
	]
	: mul_expr
	| add_expr AddOp mul_expr
	;

mul_expr
	locals [
		HExpression *hexpr,
	]
	: unary_expr
	| mul_expr MulOp unary_expr
	;

unary_expr
	locals [
		HExpression *hexpr,
	]
	: primary_expr
	| (AddOp | NotSign) unary_expr
	| ID LPar func_args? RPar
	;


primary_expr
	locals [
		HExpression *hexpr,
	]
	: LPar expr RPar
	| lval
	| const_expr
	;

const_expr
	locals [
		Btype type,
	]
	: number
	| Bool_const
	;

number
	locals [
		number_type_t val,
	]
	: AddOp? (Int_const | Double_const | Float_const)
	;


func_args
	: expr (Comma expr)*
	;


/* lexer */

VOID: 'void';
INT: 'int';
BOOL: 'bool';
FLOAT: 'float';
DOUBLE: 'double';

CONST: 'const';
IF: 'if';
ELSE: 'else';
WHILE: 'while';
BREAK: 'break';
CONTINUE: 'continue';
RETURN: 'return';

MulOp: '*' | '/' | '%';
AddOp: '+' | '-';
NotSign: '!';
RelOp: '<=' | '<' | '>=' | '>';
EqOp: '==' | '!=';
LgOrOp: '||';
LgAndOp: '&&';
Semicolon: ';';
Comma: ',';
LPar: '(';
RPar: ')';
LSqr: '[';
RSqr: ']';
LCur: '{';
RCur: '}';
AssignOp: '=';

Bool_const
	: 'true'
	| 'false'
	;

New_line
	: ('\r' '\n'? | '\n')+ -> skip
	;

White_space
	: (' ' | '\t')+ -> skip
	;

Line_comment
	: '//' (~[\r\n])* -> skip
	;

Block_comment
	: '/*' .*? '*/' -> skip
	;

Int_const
	: Dec_const
	| Oct_const
	| Hex_const
	;

Float_const
	: [0-9]+ '.' [0-9]* [Ff]
	| '.' [0-9]+ [Ff]
	| ([0-9]+ '.' [0-9]* | '.' [0-9]+ | [0-9]+) [Ee] [+-]? [0-9]+ [Ff]
	;

Double_const
	: [0-9]+ '.' [0-9]*
	| '.' [0-9]+
	| ([0-9]+ '.' [0-9]* | '.' [0-9]+ | [0-9]+) [Ee] [+-]? [0-9]+
	;
/*
 * NOTE: should [+-] be allowed for the floating point number?
 * This may cause ambiguity: Unary_expr -> [+-] Unary_expr
 */

Dec_const
	: '0'
	| [1-9] [0-9]*
	;

Oct_const
	: '0' [0-7]*
	;

Hex_const
	: ('0x' | '0X') [0-9a-fA-F]+
	;

ID
	: ([a-zA-Z] | '_') ([a-zA-Z] | '_' | [0-9])*
	;
